/* $Id:action.cc $ */
/*
   Copyright (C) 2007 by Jurjen Stellingwerff <jurjen@stwerff.xs4all.nl>
   Part of the Moros Project http://moros.sourceforge.net/

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License version 3.
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY.

   See the COPYING file for more details.
*/

#define DEBUG_ROUTINE "action"

#include <sstream>
#include <fstream>
#include "../general/util.h"
#include "../general/log.h"
#include "../general/channel.h"
#include "action.h"
#include "skill.h"
#include "skill.h"
#include "../general/general.h"

action::action() {
    name="";
    description="";
    type=-1;
    deity=0;
    time=0;
    time_base=-1;
    endurance=0;
    range=0;
    aiming=0;
    energy=0;
    skills=map<string,class action_skill>();
    bonus=map<int,class action_bonus>();
    maxBonus=0;
}

// Field : name

const string action::getName() const { return name; }
void action::setName(const string v) { name=v; }

// Field : description

const string action::getDescription() const { return description; }
void action::setDescription(const string v) { description=v; }

// Field : type

const string actionType[]={"free","tools","transport","building"};
const int action::getType() const { return type; }
const string action::showType() const {
    if (type<0) return "";
    else return actionType[type];
}
void action::setType(const int  v) {
    if (v<0 || (unsigned int)v >= sizeof actionType/sizeof actionType[0]) type=-1;
    else type=v;
}
void action::setType(const string v) {
    type=-1;
    loopArray(actionType, tp, string) {
        if (v==*tp) {type=tp_i; break; }
    }
    if (type==-1) dbg_state("Enumerate action.type %s has unknown '%s'", getName().c_str(), v.c_str());
}

// Field : deity

class skill& action::getDeity() const { if (deity==0) throw nullpointer(); return *deity; }
void action::setDeity(class skill &v) { deity=&v; }

// Field : time

const int action::getTime() const { return time; }
void action::setTime(const int v) { time=v; }

// Field : time_base

const string actionTime_base[]={"second","minute","hour","day","week","month","year"};
const int action::getTime_base() const { return time_base; }
const string action::showTime_base() const {
    if (time_base<0) return "";
    else return actionTime_base[time_base];
}
void action::setTime_base(const int  v) {
    if (v<0 || (unsigned int)v >= sizeof actionTime_base/sizeof actionTime_base[0]) time_base=-1;
    else time_base=v;
}
void action::setTime_base(const string v) {
    time_base=-1;
    loopArray(actionTime_base, tp, string) {
        if (v==*tp) {time_base=tp_i; break; }
    }
    if (time_base==-1) dbg_state("Enumerate action.time_base %s has unknown '%s'", getName().c_str(), v.c_str());
}

// Field : endurance

const int action::getEndurance() const { return endurance; }
void action::setEndurance(const int v) { endurance=v; }

// Field : range

const int action::getRange() const { return range; }
void action::setRange(const int v) { range=v; }

// Field : aiming

const int action::getAiming() const { return aiming; }
void action::setAiming(const int v) { aiming=v; }

// Field : energy

const int action::getEnergy() const { return energy; }
void action::setEnergy(const int v) { energy=v; }

// Field : skills

class action_skill& action::getSkills(const string v) { return skills[v]; }
map<string,class action_skill>& action::listSkills() { return skills; }
void action::setSkills(class action_skill &v) {
    skills[v.getFirst_skill().getName()]=v;
}
void action::delSkills(const string v) { skills.erase(v); }
const bool action::isSkills(const string v) const { return skills.count(v)>0; }

// Field : bonus

class action_bonus& action::getBonus(const int v) { return bonus[v]; }
map<int,class action_bonus>& action::listBonus() { return bonus; }
void action::setBonus(class action_bonus &v) {
    if (v.getNr()==0) v.setNr(++maxBonus);
    bonus[v.getNr()]=v;
}
void action::delBonus(const int v) { bonus.erase(v); }
const bool action::isBonus(const int v) const { return bonus.count(v)>0; }

void action::save(class channel &ch) {
    ch.write("action", "");
    ch.write("n", getName());
    ch.write("d", getDescription());
    ch.write("t", getType());
    ch.write("de", getDeity().getName());
    ch.write("ti", getTime());
    ch.write("ti6", getTime_base());
    ch.write("e", getEndurance());
    ch.write("r", getRange());
    ch.write("a", getAiming());
    ch.write("en", getEnergy());
    loopActionSkills(this, s_skills) {
        s_skills->save(ch);
    }
    loopActionBonus(this, s_bonus) {
        s_bonus->save(ch);
    }
}

void action::load(class channel &ch) {
    if (ch.key=="n" || ch.key=="name") { setName(ch.value); ch.next(); }
    if (ch.key=="d" || ch.key=="description") { setDescription(ch.value); ch.next(); }
    if (ch.key=="t" || ch.key=="type") { setType(ch.value); ch.next(); }
    if (ch.key=="de" || ch.key=="deity") {
        if (!skill_repo.isSkill(ch.value)) {
            dbg_state("link unknown skill %s", ch.value.c_str());
            class skill rec;
            rec.setName(ch.value);
            skill_repo.setSkill(rec);
        }
        setDeity(skill_repo.getSkill(ch.value));
        ch.next();
    }
    if (ch.key=="ti" || ch.key=="time") { setTime(atoi(ch.value.c_str())); ch.next(); }
    if (ch.key=="ti6" || ch.key=="time_base") { setTime_base(ch.value); ch.next(); }
    if (ch.key=="e" || ch.key=="endurance") { setEndurance(atoi(ch.value.c_str())); ch.next(); }
    if (ch.key=="r" || ch.key=="range") { setRange(atoi(ch.value.c_str())); ch.next(); }
    if (ch.key=="a" || ch.key=="aiming") { setAiming(atoi(ch.value.c_str())); ch.next(); }
    if (ch.key=="en" || ch.key=="energy") { setEnergy(atoi(ch.value.c_str())); ch.next(); }
    while (ch.key=="action_skill" || ch.key=="skills") {
        ch.next();
        class action_skill rec;
        rec.load(ch);
        setSkills(rec);
    }
    while (ch.key=="action_bonus" || ch.key=="bonus") {
        ch.next();
        class action_bonus rec;
        rec.load(ch);
        setBonus(rec);
    }
}

void action::xml(ofstream &f, int l, int r) {
    f << ncpy(l, "  ") << "<action name=\"" << xml_encode(getName()) <<
    "\" description=\"" << xml_encode(getDescription()) <<
    "\" type=\"" << showType() <<
    "\" time=\"" << getTime() <<
    "\" time_base=\"" << showTime_base() <<
    "\" endurance=\"" << getEndurance() <<
    "\" range=\"" << getRange() <<
    "\" aiming=\"" << getAiming() <<
    "\" energy=\"" << getEnergy() <<
    "\">" << endl;
    if (r<2) try {
        getDeity().xml(f,l+1,r+1);
    } catch (nullpointer n) {}
    if (r==0) {
        loopActionSkills(this, s_skills) {
            s_skills->xml(f,l+1);
        }
        loopActionBonus(this, s_bonus) {
            s_bonus->xml(f,l+1);
        }
    }
    f << ncpy(l, "  ") << "</action>" << endl;
}

// Repository

actionRepository action_repo;

class action& actionRepository::getAction(const string v) { return actions[v]; }
void actionRepository::setAction(class action& v) {
    if (v.getName()=="") throw unnamed();
    actions[v.getName()]=v;
}
map<string,class action>& actionRepository::listAction() { return actions; }
void actionRepository::delAction(const string v) { actions.erase(v); }
const bool actionRepository::isAction(const string v) const { return actions.count(v)>0; }
void actionRepository::save(class channel &ch) {
    loopActions(rec) {
        rec->save(ch);
    }
}

bool actionRepository::load(class channel &ch) {
    if (ch.key=="action") {
        ch.next();
        class action rec;
        rec.load(ch);
        setAction(rec);
        return true;
    }
    return false;
}
